/*opyright (C) 2007 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *
 * Based on code:
 *
 * Copyright (c) 1995 - 2000 Kungliga Tekniska H?gskolan
 * (Royal Institute of Technology, Stockholm, Sweden).
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#define _XOPEN_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <termios.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <keyutils.h>
//#include <rpc/xdr.h>
#include <kerberosIV/des.h>
#include <getopt.h>
#include <sys/types.h>
#include <netdb.h>


#define FLAG_P 0x00000001
#define FLAG_M 0x00000002
#define FLAG_A 0x00000004
 #define BUFFERSIZE 1024

#define KA_SERVICE 731
#define KA_TICKET_GRANTING_SERVICE 732

#define OSERROR(X, Y) do { if ((long)(X) == -1) { perror(Y); exit(1); } } while(0)

struct sockaddr_rxrpc {
       sa_family_t     srx_family;     /* address family */
       unsigned short  srx_service;    /* service desired */
       unsigned short  transport_type; /* type of transport socket (SOCK_DGRAM) */
       unsigned short  transport_len;  /* length of transport address */
       union {
               sa_family_t family;             /* transport address family */
               struct sockaddr_in sin;         /* IPv4 transport address */
               struct sockaddr_in6 sin6;       /* IPv6 transport address */
       } transport;
};

#define AF_RXRPC                33
#define PF_RXRPC                AF_RXRPC
#define SOL_RXRPC               272
#define RXRPC_USER_CALL_ID      1       /* User call ID specifier */
#define RXRPC_ABORT             2       /* Abort request / notification */
#define RXRPC_ACK               3       /* [Server] RPC op final ACK received */
#define RXRPC_RESPONSE          4       /* [Server] security response received */
#define RXRPC_NET_ERROR         5       /* network error received */
#define RXRPC_BUSY              6       /* server busy received */
#define RXRPC_LOCAL_ERROR       7       /* local error generated */
#define RXRPC_PREPARE_CALL_SLOT 8       /* Propose user call ID specifier for next call */
#define RXRPC_SECURITY_KEY              1       /* [clnt] set client security key */
#define RXRPC_SECURITY_KEYRING          2       /* [srvr] set ring of server security keys */
#define RXRPC_EXCLUSIVE_CONNECTION      3       /* [clnt] use exclusive RxRPC connection */
#define RXRPC_MIN_SECURITY_LEVEL        4       /* minimum security level */

#define OSERROR(X, Y) do { if ((long)(X) == -1) { perror(Y); exit(1); } } while(0)

static const unsigned char local_addr[4] = { 0, 0, 0, 0 };
//static const unsigned char remote_addr[4] = { 90, 155, 74, 22 };
 unsigned char remote_addr[4] = { 192,168,76,132};

#define RXRPC_ADD_CALLID(control, ctrllen, id)                          \
do {                                                                    \
       struct cmsghdr *__cmsg;                                         \
       __cmsg = (void *)(control) + (ctrllen);                         \
       __cmsg->cmsg_len        = CMSG_LEN(sizeof(unsigned long));      \
       __cmsg->cmsg_level      = SOL_RXRPC;                            \
       __cmsg->cmsg_type       = RXRPC_USER_CALL_ID;                   \
       *(unsigned long *)CMSG_DATA(__cmsg) = (id);                     \
       (ctrllen) += __cmsg->cmsg_len;                                  \
                                                                       \
} while (0)

#define RXRPC_ADD_ABORT(control, ctrllen, abort_code)                   \
do {                                                                    \
       struct cmsghdr *__cmsg;                                         \
       __cmsg = (void *)(control) + (ctrllen);                         \
       __cmsg->cmsg_len        = CMSG_LEN(sizeof(unsigned long));      \
       __cmsg->cmsg_level      = SOL_RXRPC;                            \
       __cmsg->cmsg_type       = RXRPC_ABORT;                          \
       *(unsigned long *)CMSG_DATA(__cmsg) = (abort_code);             \
       (ctrllen) += __cmsg->cmsg_len;                                  \
                                                                       \
} while (0)

const char KA_GETTGT_REQ_LABEL[] = "gTGS";
const char KA_GETTGT_RPL_LABEL[] = "tgsT";
const char KA_GETTKT_RPL_LABEL[] = "gtkt";
#define KAA_Authenticate        21
#define KAA_AuthenticateV2      22
#define KAT_GetToken            23

struct ka_ticket {
       des_cblock      session_key;
       time_t          end_time;
       int             kvno;
       int             ticket_len;
       const char      *name;
       const char      *instance;
       const char      *cell;
       const char      *server_name;
       const char      *server_instance;
       const void      *ticket;
       char            _reply[0];
};

struct rxrpc_key_sec2_v1 {
       uint32_t        kver;                   /* key payload interface version */
       uint16_t        security_index;         /* RxRPC header security index */
       uint16_t        ticket_length;          /* length of ticket[] */
       uint32_t        expiry;                 /* time at which expires */
       uint32_t        kvno;                   /* key version number */
       uint8_t         session_key[8];         /* DES session key */
       uint8_t         ticket[0];              /* the encrypted ticket */
};

/*****************************************************************************/
/*
 * dump the control messages
 */
static void dump_cmsg(struct msghdr *msg)
{
       struct cmsghdr *cmsg;
       unsigned long user_id;
       unsigned char *p;
       int abort_code;
       int n;

       for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
               n = cmsg->cmsg_len - CMSG_ALIGN(sizeof(*cmsg));
               p = CMSG_DATA(cmsg);

               printf("CMSG: %zu: ", cmsg->cmsg_len);

               if (cmsg->cmsg_level == SOL_RXRPC) {
                       switch (cmsg->cmsg_type) {
                       case RXRPC_USER_CALL_ID:
                               printf("RXRPC_USER_CALL_ID: ");
                               if (n != sizeof(user_id))
                                       goto dump_data;
                               memcpy(&user_id, p, sizeof(user_id));
                               printf("%lx\n", user_id);
                               continue;

                       case RXRPC_ABORT:
                               printf("RXRPC_ABORT: ");
                               if (n != sizeof(abort_code))
                                       goto dump_data;
                               memcpy(&abort_code, p, sizeof(abort_code));
                               printf("%d\n", abort_code);
                               continue;

                       case RXRPC_ACK:
                               printf("RXRPC_ACK");
                               if (n != 0)
                                       goto dump_data_colon;
                               goto print_nl;

                       case RXRPC_RESPONSE:
                               printf("RXRPC_RESPONSE");
                               if (n != 0)
                                       goto dump_data_colon;
                               goto print_nl;

                       case RXRPC_NET_ERROR:
                               printf("RXRPC_NET_ERROR: ");
                               if (n != sizeof(abort_code))
                                       goto dump_data;
                               memcpy(&abort_code, p, sizeof(abort_code));
                               printf("%s\n", strerror(abort_code));
                               continue;

                       case RXRPC_BUSY:
                               printf("RXRPC_BUSY");
                               if (n != 0)
                                       goto dump_data_colon;
                               goto print_nl;

                       case RXRPC_LOCAL_ERROR:
                               printf("RXRPC_LOCAL_ERROR: ");
                               if (n != sizeof(abort_code))
                                       goto dump_data;
                               memcpy(&abort_code, p, sizeof(abort_code));
                               printf("%s\n", strerror(abort_code));
                               continue;

                       default:
                               break;
                       }
               }

               printf("l=%d t=%d", cmsg->cmsg_level, cmsg->cmsg_type);

       dump_data_colon:
               printf(": ");
       dump_data:
               printf("{");
               for (; n > 0; n--, p++)
                       printf("%02x", *p);

       print_nl:
               printf("}\n");
       }
}

void dump_data(void *p, size_t len)
{
#define INT_PER_LINE (8 * 4)
       size_t loop;
       unsigned char buf[41], *b, ch, yoffs;

       yoffs = 0;
       b = buf;
       for (loop = 0; loop < len; loop++) {
               if (!yoffs) {
                       printf("%08zx: ", loop);
                       yoffs = 1;
               }
               ch = *(const char *) p;
               printf("%02x", ch);
               *b++ = isprint(ch) ? ch : '.';
               p++;
               if (loop % INT_PER_LINE == INT_PER_LINE - 1) {
                       *b = 0;
                       printf(" %s\n", buf);
                       b = buf;
                       yoffs = 0;
               } else if (loop % 4 == 3) {
                       putchar(' ');
               }
       }

       if (loop % INT_PER_LINE > 0) {
               for (loop %= INT_PER_LINE; loop < INT_PER_LINE; loop++) {
                       printf("  ");
                       if (loop % 4 == 3)
                               putchar(' ');
               }
               *b = 0;
               printf("%s\n", buf);
       }
}

/*****************************************************************************/
/*
 *
 */
int ka_RPC(int service, const void *request, size_t reqlen, void *reply, size_t *_replen)
{
       struct sockaddr_rxrpc srx;
       struct msghdr msg;
       struct iovec iov[1];
       size_t ctrllen, replen;
       unsigned char control[4096];
       void *preply;
       int client, ret;

       client = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);
       OSERROR(client, "socket");

       /* the authentication is associated with the virtual
        * connection, so we need to open an exclusive channel */
       ret = setsockopt(client, SOL_RXRPC, RXRPC_EXCLUSIVE_CONNECTION,
                        NULL, 0);
       OSERROR(ret, "setsockopt");

       /* bind an address to the local endpoint */
       srx.srx_family = AF_RXRPC;
       srx.srx_service = 0; /* it's a client */
       srx.transport_type = SOCK_DGRAM;
       srx.transport_len = sizeof(srx.transport.sin);
       srx.transport.sin.sin_family = AF_INET;
       srx.transport.sin.sin_port = htons(7001);
       memcpy(&srx.transport.sin.sin_addr, &local_addr, 4);

       ret = bind(client, (struct sockaddr *) &srx, sizeof(srx));
       OSERROR(ret, "bind");

       /* connect to the remote server */
       srx.srx_family = AF_RXRPC;
       srx.srx_service = service;
       srx.transport_type = SOCK_DGRAM;
       srx.transport_len = sizeof(srx.transport.sin);
       srx.transport.sin.sin_family = AF_INET;
       srx.transport.sin.sin_port = htons(7004);
       memcpy(&srx.transport.sin.sin_addr, &remote_addr, 4);

       ret = connect(client, (struct sockaddr *) &srx, sizeof(srx));
       OSERROR(ret, "connect");

       /* request an operation */
       ctrllen = 0;
       RXRPC_ADD_CALLID(control, ctrllen, 0x12345);

       iov[0].iov_base = (void *) request;
       iov[0].iov_len = reqlen;

       msg.msg_name            = NULL;
       msg.msg_namelen         = 0;
       msg.msg_iov             = iov;
       msg.msg_iovlen          = 1;
       msg.msg_control         = control;
       msg.msg_controllen      = ctrllen;
       msg.msg_flags           = 0;

       ret = sendmsg(client, &msg, 0);
       if (ret == -1 && (errno == ENOANO || errno == ECONNABORTED))
               perror("sendmsg/data");
       else
               OSERROR(ret, "sendmsg/data");

       /* wait for a reply */
       preply = reply;
       replen = 0;
       while (replen < *_replen) {
               iov[0].iov_base = preply;
               iov[0].iov_len = *_replen - replen;

               msg.msg_name    = NULL;
               msg.msg_namelen = 0;
               msg.msg_iov     = iov;
               msg.msg_iovlen  = 1;
               msg.msg_control = control;
               msg.msg_controllen = sizeof(control);
               msg.msg_flags   = 0;

               ret = recvmsg(client, &msg, 0);
               OSERROR(ret, "recvmsg");

               printf("RECV: %d [fl:%d]\n", ret, msg.msg_flags);
               printf("CMSG: %zu\n", msg.msg_controllen);
               printf("IOV: %zu [0]=%zu\n", msg.msg_iovlen, iov[0].iov_len);
               dump_cmsg(&msg);

               preply += ret;
               replen += ret;

               if (msg.msg_flags & MSG_EOR)
                       break;
       }

       *_replen = replen;
       close(client);
       return 0;
}

/*****************************************************************************/
/*
 * authenticate this RxRPC virtual connection with the KA server
 */
int ka_Authenticate(const char *principal,
                   const char *realm,
                   int service,
                   des_cblock _key,
                   time_t start_time,
                   time_t end_time,
                   struct ka_ticket **_tgt)
{
       struct ka_ticket *tgt;
       des_key_schedule sched;
       char buffer[16384] __attribute__((aligned(4)));
       char data[8], *p, *end, *q;
       unsigned int tmp;
       des_cblock key;
       size_t len;
       time_t challtime;
       int ret;

       memcpy(key, _key, sizeof(key));
       ret = des_key_sched(key, sched);
       OSERROR(ret, "des_key_sched");

       tmp = htonl(start_time);
       memcpy(data, &tmp, 4);
       memcpy(data + 4, KA_GETTGT_REQ_LABEL, 4);

       des_pcbc_encrypt((void *) data, (void *) data,
                        sizeof(data), sched, &key, DES_ENCRYPT);

       /* marshall the arguments */
       p = buffer;
       *(unsigned int *)p = htonl(KAA_AuthenticateV2);
       p += 4;

       len = strlen(principal);
       *(unsigned int *)p = htonl(len);
       p += 4;
       memcpy(p, principal, len);
       p += len;
       while ((unsigned long) p & 3)
               *p++ = 0;

       realm = NULL;
       if (realm) {
               len = strlen(realm);
               *(unsigned int *)p = htonl(len);
               p += 4;
               memcpy(p, realm, len);
               p += len;
               while ((unsigned long) p & 3)
                       *p++ = 0;
       } else {
               *(unsigned int *)p = htonl(0);
               p += 4;
       }

       *(unsigned int *)p = htonl(start_time);
       p += 4;
       *(unsigned int *)p = htonl(end_time);
       p += 4;

       len = sizeof(data);
       *(unsigned int *)p = htonl(len);
       p += 4;
       memcpy(p, data, len);
       p += len;
       while ((unsigned long) p & 3)
               *p++ = 0;

       *(unsigned int *)p = htonl(0x3044);
       p += 4;
       *(unsigned int *)p = htonl(0);
       p += 4;

       /* make the call */
       len = sizeof(buffer);
       ret = ka_RPC(KA_SERVICE, buffer, p - buffer, buffer, &len);
       OSERROR(buffer, "ka_RPC");

       /* unmarshall the reply */
       p = buffer;
       if (len < 8)
               goto bad_reply;

       if (ntohl(*(unsigned int *)p) != 0x3044)
               goto bad_reply;
       p += 4;

       len -= 8;
       tmp = ntohl(*(unsigned int *)p);
       p += 4;

       if (((tmp + 3) & ~3) != len)
               goto bad_reply;

       if (len < 32 + 5 + 8)
               goto bad_reply;

       tgt = malloc(sizeof(struct ka_ticket) + len);
       if (!tgt) {
               perror("malloc");
               exit(1);
       }

       des_pcbc_encrypt((void *) p, (void *) tgt->_reply, len, sched, &key, DES_DECRYPT);
       dump_data(tgt->_reply, len);
       end = tgt->_reply + len;

       /* decode the reply */
       memcpy(&tgt->session_key, tgt->_reply + 8, 8);
       challtime       = ntohl(*(unsigned *)(tgt->_reply + 4));
       tgt->end_time   = ntohl(*(unsigned *)(tgt->_reply + 20));
       tgt->kvno       = ntohl(*(unsigned *)(tgt->_reply + 24));
       tgt->ticket_len = ntohl(*(unsigned *)(tgt->_reply + 28));

       p = tgt->_reply + 32;

       tgt->name = p;
       q = memchr(p, 0, end - p);
       if (!q)
               goto bad_reply;
       p = q + 1;

       tgt->instance = p;
       q = memchr(p, 0, end - p);
       if (!q)
               goto bad_reply;
       p = q + 1;

       tgt->cell = p;
       q = memchr(p, 0, end - p);
       if (!q)
               goto bad_reply;
       p = q + 1;

       tgt->server_name = p;
       q = memchr(p, 0, end - p);
       if (!q)
               goto bad_reply;
       p = q + 1;

       tgt->server_instance = p;
       q = memchr(p, 0, end - p);
       if (!q)
               goto bad_reply;
       p = q + 1;

       tgt->ticket = p;
       if (tgt->ticket_len > end - p)
               goto bad_reply;
       p += tgt->ticket_len;

       /* validate the reply */
       if (memcmp(p, KA_GETTGT_RPL_LABEL, sizeof(KA_GETTGT_RPL_LABEL) - 1) != 0)
               goto bad_reply;

       if (challtime != start_time + 1)
               goto bad_reply;

       printf("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x\n",
              tgt->session_key[0], tgt->session_key[1], tgt->session_key[2], tgt->session_key[3],
              tgt->session_key[4], tgt->session_key[5], tgt->session_key[6], tgt->session_key[7]);
       printf("KVNO: %u\n", tgt->kvno);
       printf("TLEN: %u\n", tgt->ticket_len);
       printf("NAME: %s\n", tgt->name);
       printf("INST: %s\n", tgt->instance);
       printf("CELL: %s\n", tgt->cell);
       printf("SNAM: %s\n", tgt->server_name);
       printf("SINS: %s\n", tgt->server_instance);

       *_tgt = tgt;
       return 0;

bad_reply:
       fprintf(stderr, "Unmarshalling failure\n");
       errno = EBADMSG;
       return -1;
}

/*****************************************************************************/
/*
 *
 */
int ka_GetToken(const char *name,
               const char *instance,
               time_t start_time,
               time_t end_time,
               const struct ka_ticket *tgt,
               const char *auth_domain,
               struct ka_ticket **_ticket)
{
       struct ka_ticket *ticket;
       des_key_schedule sched;
       unsigned int tmp;
       des_cblock key;
       char buffer[16384] __attribute__((aligned(4)));
       char tdata[8], data[8], *p, *end, *q;
       size_t len;
       int ret;

       memcpy(key, tgt->session_key, sizeof(key));
       dump_data(key, 8);
       ret = des_key_sched(key, sched);
       OSERROR(ret, "des_key_sched");

       tmp = htonl(start_time);
       memcpy(tdata, &tmp, 4);
       tmp = htonl(tgt->end_time);
       memcpy(tdata + 4, &tmp, 4);

       dump_data(tdata, 8);
       des_ecb_encrypt((void *) tdata, (void *) data, sched, DES_ENCRYPT);
       dump_data(data, 8);
       memset(tdata, 0xff, 8);
       ret = des_key_sched(key, sched);
       des_pcbc_encrypt((void *) data, (void *) tdata, sizeof(data),
                        sched, &key, DES_DECRYPT);
       dump_data(tdata, 8);

       /* marshall the arguments */
       p = buffer;
       *(unsigned int *)p = htonl(KAT_GetToken);
       p += 4;
       *(unsigned int *)p = htonl(tgt->kvno);
       p += 4;

       len = strlen(auth_domain);
       *(unsigned int *)p = htonl(len);
       p += 4;
       memcpy(p, auth_domain, len);
       p += len;
       while ((unsigned long) p & 3)
               *p++ = 0;

       len = tgt->ticket_len;
       *(unsigned int *)p = htonl(len);
       p += 4;
       memcpy(p, tgt->ticket, len);
       p += len;
       while ((unsigned long) p & 3)
               *p++ = 0;

       len = strlen("afs"); // tgt->name
       *(unsigned int *)p = htonl(len);
       p += 4;
       memcpy(p, "afs", len);
       p += len;
       while ((unsigned long) p & 3)
               *p++ = 0;

       len = strlen(tgt->instance);
       *(unsigned int *)p = htonl(len);
       p += 4;
       memcpy(p, tgt->instance, len);
       p += len;
       while ((unsigned long) p & 3)
               *p++ = 0;

       len = sizeof(data);
       *(unsigned int *)p = htonl(len);
       p += 4;
       memcpy(p, data, len);
       p += len;

       *(unsigned int *)p = htonl(0x3044);
       p += 4;
       *(unsigned int *)p = htonl(0);
       p += 4;

       /* make the call */
       len = sizeof(buffer);
       ret = ka_RPC(KA_TICKET_GRANTING_SERVICE,
                    buffer, p - buffer, buffer, &len);
       OSERROR(buffer, "ka_RPC");

       /* unmarshall the reply */
       p = buffer;
       if (len < 8)
               goto bad_reply;

       if (ntohl(*(unsigned int *)p) != 0x3044)
               goto bad_reply;
       p += 4;

       len -= 8;
       tmp = ntohl(*(unsigned int *)p);
       p += 4;

       if (((tmp + 3) & ~3) != len)
               goto bad_reply;

       if (len < 32 + 5 + 8)
               goto bad_reply;

       ticket = malloc(sizeof(struct ka_ticket) + len);
       if (!ticket) {
               perror("malloc");
               exit(1);
       }

       des_pcbc_encrypt((void *) p, (void *) ticket->_reply, len, sched, &key, DES_DECRYPT);
       dump_data(ticket->_reply, len);
       end = ticket->_reply + len;

       /* decode the reply */
       memcpy(&ticket->session_key, ticket->_reply + 8, 8);
       ticket->end_time        = ntohl(*(unsigned *)(ticket->_reply + 20));
       ticket->kvno            = ntohl(*(unsigned *)(ticket->_reply + 24));
       ticket->ticket_len      = ntohl(*(unsigned *)(ticket->_reply + 28));

       p = ticket->_reply + 32;

       ticket->name = p;
       q = memchr(p, 0, end - p);
       if (!q)
               goto bad_reply;
       p = q + 1;

       ticket->instance = p;
       q = memchr(p, 0, end - p);
       if (!q)
               goto bad_reply;
       p = q + 1;

       ticket->cell = p;
       q = memchr(p, 0, end - p);
       if (!q)
               goto bad_reply;
       p = q + 1;

       ticket->server_name = p;
       q = memchr(p, 0, end - p);
       if (!q)
               goto bad_reply;
       p = q + 1;

       ticket->server_instance = p;
       q = memchr(p, 0, end - p);
       if (!q)
               goto bad_reply;
       p = q + 1;

       ticket->ticket = p;
       if (ticket->ticket_len > end - p)
               goto bad_reply;
       p += ticket->ticket_len;

       /* validate the reply */
       if (memcmp(p, KA_GETTKT_RPL_LABEL, sizeof(KA_GETTKT_RPL_LABEL) - 1) != 0)
               goto bad_reply;

       printf("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x\n",
              ticket->session_key[0], ticket->session_key[1], ticket->session_key[2], ticket->session_key[3],
              ticket->session_key[4], ticket->session_key[5], ticket->session_key[6], ticket->session_key[7]);
       printf("KVNO: %u\n", ticket->kvno);
       printf("TLEN: %u\n", ticket->ticket_len);
       printf("NAME: %s\n", ticket->name);
       printf("INST: %s\n", ticket->instance);
       printf("CELL: %s\n", ticket->cell);
       printf("SNAM: %s\n", ticket->server_name);
       printf("SINS: %s\n", ticket->server_instance);

       *_ticket = ticket;
       return 0;

bad_reply:
       fprintf(stderr, "Unmarshalling failure\n");
       errno = EBADMSG;
       return -1;
}

/*****************************************************************************/
/*
 *
 */
int ka_GetServerToken(const char *name,
                     const char *instance,
                     const char *cell,
                     unsigned lifetime,
                     struct ka_ticket *tgt,
                     struct ka_ticket **_ticket)
{
       struct timeval tv;
       int ret;

       ret = gettimeofday(&tv, NULL);
       OSERROR(ret, "gettimeofday");

       ret = ka_GetToken(name, instance,
                         tv.tv_sec, tv.tv_sec + lifetime, tgt,
                         "CAMBRIDGE.REDHAT.COM", /* auth domain */
                         _ticket);
       OSERROR(ret, "ka_GetToken");
       return ret;
}

/*****************************************************************************/
/*
 *
 */
int ka_UserAuthenticateGeneral(unsigned long flags,
                              const char *principal,
                              const char *instance,
                              const char *realm,
                              const char *password,
                              unsigned lifetime,
                              long spare1,
                              long spare2,
                              char **reason)
{
       struct rxrpc_key_sec2_v1 *payload;
       struct timeval tv;
       des_cblock key;
       struct ka_ticket *tgt, *ticket;
       char description[256];
       size_t plen;
       int ret;

       afs_string_to_key((char *) password, (char *) realm, key);

       ret = gettimeofday(&tv, NULL);
       OSERROR(ret, "gettimeofday");

       ret = ka_Authenticate(principal, instance, KA_TICKET_GRANTING_SERVICE,
                             key, tv.tv_sec, tv.tv_sec + lifetime, &tgt);
       OSERROR(ret, "ka_Authenticate");

       printf("\n----\n");
       ret = ka_GetServerToken(tgt->name, tgt->instance, tgt->cell, 3600 * 60 * 60,
                               tgt, &ticket);
       OSERROR(ret, "ka_GetServerToken");

       plen = sizeof(*payload) + ticket->ticket_len;
       payload = calloc(1, plen + 4);
       if (!payload) {
               perror("calloc");
               exit(1);
       }

       /* use version 1 of the key data interface */
       payload->kver           = 1;
       payload->security_index = 2;
       payload->ticket_length  = ticket->ticket_len;
       payload->expiry         = ticket->end_time;
       payload->kvno           = ticket->kvno;
       memcpy(payload->session_key, ticket->session_key, 8);
       memcpy(payload->ticket, ticket->ticket, ticket->ticket_len);

       sprintf(description, "afs@CAMBRIDGE.REDHAT.COM");
       ret = add_key("rxrpc", description, payload, plen, KEY_SPEC_SESSION_KEYRING);
       OSERROR(ret, "add_key");

       return 0;
}

static inline void hosttoip(unsigned char * addr,const char* sour){
	
        struct hostent *h;
        if ((h=gethostbyname(sour)) == NULL) {
                herror("klog\n\tCan't connect to host, the error is:");
                exit(1);
        }
        printf("klog\n\tHost name : %s ", h->h_name);
        printf("\tIP Address : %s\n",inet_ntoa(*((struct in_addr *)h->h_addr)));

	memcpy(addr,h->h_addr,4);
	
}

static inline void print_ver()
{
	printf("klog, version 1.0.\n");
	exit(0);
}
static inline void print_help()
{
	printf("klog\n"
	//	"g:u:p:i:m:l:f:s:r:a:vh?"
		"\tklog [-g flags] [-u username] -p password -r realm [-i instance] [-l lifetime]\n"
		"\t[-f spare1] [-s spare2] -a remoteaddr [-v] [-h]\n"
		"\n\n"
		"option:\n"
		"\tf	flags 			defalut value is 0\n"
		"\tu	username 		defalut value is \"admin\"\n"
		"\tp	password		password for username\n"
		"\tr	realm			real server name\n"
		"\ti	instance		instance\n"
		"\tl	lifetime		lifetime to set,defalut value is 3600*60*60\n"
		"\tf	spare1			defalult value is 0\n"
		"\ts	spare2			defalult value is 0\n"
		"\ta 	remoteaddr		remote server address\n"
		"\tv	version			version number\n"
		"\th	help			help\n"
		"\n");
	exit(0);
	
}
static inline void return_default()
{
	printf("klog\n\tThe parameters inputed are not right.Please input \"-h\" for help.\n");
	exit(0);
}


static inline void test_opt_needed (unsigned int optf)
{	
	unsigned int flag = FLAG_P|FLAG_M|FLAG_A;
	if(optf == flag )
		return;
	const char errs[] = "klog error\n\tThe paremetes are not enough,more paremetes: ";
	const char erre[] = "is(are) needed to klog.\n\tInput \"-h\" for help infomation.\n";
	
	printf("%s%s%s%s%s",
		errs,
		(optf&FLAG_P ? "" : "-p PASSWORD,"),
		(optf&FLAG_M ? "" : "-m REALM,"),
		(optf&FLAG_A ? "" : "-a REMOTEADDR,"),
		erre);
	exit(0);
}

#define zstrcpy(dest,src) \
	memset(dest,0,BUFFERSIZE);\
	strcpy(dest,src)
	
/*****************************************************************************/
/*
 *
 */


 
int main(int argc, char *argv[])
{
      	int ret;
	unsigned int optf  = 0;
	int option;
	int option_index;
	int opt_needed = 0;
	/*parameters used for ka_UserAuthenticateGeneral*/
	unsigned long flags = 0;
	char username[BUFFERSIZE];
	char password[BUFFERSIZE];
	char instance[BUFFERSIZE];
	char realm[BUFFERSIZE];
	char reason[BUFFERSIZE];
	char remoteaddrc[BUFFERSIZE];
	unsigned  lifetime = 3600 * 60 *60;
	long spare1 =0;
	long spare2 =0;
	/*set the parameters the default values.*/
	
	zstrcpy(username, "admin");
	zstrcpy(instance, "");
	
    	const char *optstring 	 = "g:u:p:i:m:l:f:s:r:a:vh?";
	struct option longopts[] = {
        {"flags", 		required_argument, 	NULL, 'g'},
        {"username", 		required_argument, 	NULL, 'u'},
        {"password",		required_argument, 	NULL, 'p'},
        {"instance",		required_argument, 	NULL, 'i'},
        {"realm",		required_argument, 	NULL, 'm'},
        {"lifetime",		required_argument, 	NULL, 'l'},
        {"spare1",		required_argument, 	NULL, 'f'},
        {"spare2",		required_argument, 	NULL, 's'},
        {"reason",		required_argument, 	NULL, 'r'},
        {"remoteaddr",		required_argument, 	NULL, 'a'},
        {"version", 		no_argument, 		NULL, 'v'},
        {"help", 		no_argument, 		NULL, 'h'},
        {0, 0, 0, 0}
    	};

    	while ((option = 
			getopt_long(argc, argv, optstring, longopts, &option_index)) != -1)
			switch (option) {
			case 'g':
			    	flags = strtol(optarg,NULL,10);
			    	break;
			case 'u':
			    	zstrcpy(username,optarg);
				break;
			case 'p':
			    	optf |= FLAG_P;
			    	zstrcpy(password,optarg);
			    	break;
			case 'i':
			    	zstrcpy(instance,optarg);
			    	break;
			case 'm':
				optf |= FLAG_M;
				zstrcpy(realm,optarg);
				break;
			case 'l':
				lifetime = strtoul(optarg,NULL,0);
				break;
			case 'f':
				spare1 = strtol(optarg,NULL,0);
				break;
			case 's':
				spare2 = strtol(optarg,NULL,0);
				break;
			case 'r':
				zstrcpy(reason,optarg);
				break;
			case 'a':
				optf |= FLAG_A;
				zstrcpy(remoteaddrc,optarg);
				break;
			case 'v':
				print_ver();
				break;
			case 'h':
			case '?':
				print_help();
				break;
			default:
				return_default();
            			break;
        }
			
	test_opt_needed(optf);
	hosttoip(remote_addr,remoteaddrc);
	
	printf("klog\n\tusername:%s\t "
		 "pwd:%s \t instance:%s\t realm:%s\t reason:%s\n\n",
		 username,
		 password,
		 instance,
		 realm,
		 reason);

	   

	ret = ka_UserAuthenticateGeneral(flags,
                                        username, 
                                        instance, 
                                        realm, 
                                        password,
                                        lifetime,
                                        spare1,
                                        spare2,
                                        (char **)&reason);
	
   if (ret < 0) {
               perror(argv[0]);
               exit(1);
       }

       exit(0);
}





